<template>
	<div :class="classes">
		<label
			:class="[prefixCls + '-label']"
			:for="labelFor"
			:style="labelStyles"
			v-if="label || $slots.label"
		>
			<slot name="label">{{ label }}{{ FormInstance.colon }}</slot>
		</label>
		<div :class="[prefixCls + '-content']" :style="contentStyles">
			<slot></slot>
			<transition name="fade">
				<div
					:class="[prefixCls + '-error-tip']"
					v-if="validateState === 'error' && showMessage && FormInstance.showMessage"
				>{{ validateMessage }}</div>
			</transition>
		</div>
	</div>
</template>
<script lang="ts">
	import Anes from "../base";
	import { formKey } from "../../utils/const";
	import { Prop, Component, Watch, Inject } from "vue-property-decorator";
	import { getPropByPath } from "../../utils/assist";
	import AsyncValidator, {
		Rules,
		RuleItem,
		ValidateSource
	} from "async-validator";
	import Form from "./form.vue";

	const prefixCls = "ivu-form-item";

	@Component
	export default class FormItem extends Anes {
		@Prop({ type: String, default: "" }) label!: string;
		@Prop({ type: String, default: "" }) prop!: string;
		@Prop({ type: String, default: "" }) error!: string;
		@Prop({ type: String, default: "" }) labelFor!: string;
		@Prop({ type: Boolean, default: false }) required!: boolean;
		@Prop({ type: Boolean, default: false }) validateStatus!: boolean;
		@Prop({ type: Boolean, default: true }) showMessage!: boolean;
		@Prop(Number) labelWidth!: number;
		@Prop() rules!: RuleItem | RuleItem[];

		prefixCls = prefixCls;
		isRequired = false;
		validateState = "";
		validateMessage = "";
		validateDisabled = false;
		validator = {};
		initialValue = "";
		mounted() {
			if (this.prop) {
				//this.dispatch("iForm", "on-form-item-add", this);

				Object.defineProperty(this, "initialValue", {
					value: this.fieldValue
				});

				this.setRules();
			}
		}
		beforeDestroy() {
			//this.dispatch("iForm", "on-form-item-remove", this);
		}

		@Watch("error", { immediate: true })
		onerror(newVal: string) {
			this.validateMessage = newVal;
			this.validateState = newVal ? "error" : "";
		}

		@Watch("validateStatus")
		validateStatusHandler(newVal: boolean) {
			this.validateState = newVal ? "error" : "";
		}

		@Watch("rules")
		rulesHandler() {
			this.setRules();
		}
		@Watch("required")
		requiredHandler(newVal: boolean, oldVal: boolean) {
			this.isRequired = newVal;
			if (oldVal && !newVal) {
				this.resetField();
			}
		}

		/* 	@Inject(formKey)
								readonly ooInstance!: any; */

		get FormInstance(): Form {
			return (this.$parent as Form) ?? new Form();
		}

		get classes() {
			return [
				`${prefixCls}`,
				{
					[`${prefixCls}-required`]: this.required || this.isRequired,
					[`${prefixCls}-error`]: this.validateState === "error",
					[`${prefixCls}-validating`]: this.validateState === "validating"
				}
			];
		}

		get fieldValue() {
			const model = this.FormInstance.model;
			if (!model || !this.prop) {
				return "";
			}

			let path = this.prop;
			if (path.indexOf(":") !== -1) {
				path = path.replace(/:/, ".");
			}

			return getPropByPath(model, path).v;
		}
		get labelStyles() {
			const labelWidth =
				this.labelWidth === 0 || this.labelWidth
					? this.labelWidth
					: this.FormInstance.labelWidth;

			if (labelWidth || labelWidth === 0) {
				return `width:${labelWidth}px`;
			}
			return "";
		}
		get contentStyles() {
			const labelWidth =
				this.labelWidth === 0 || this.labelWidth
					? this.labelWidth
					: this.FormInstance.labelWidth;

			if (labelWidth || labelWidth === 0) {
				return `marginLeft:${labelWidth}px`;
			}
			return "";
		}

		setRules() {
			const rules = this.getRules();
			if (rules.length && this.required) {
				return;
			} else if (rules.length) {
				rules.every(rule => {
					this.isRequired = rule.required as boolean;
				});
			} else if (this.required) {
				this.isRequired = this.required;
			}
			this.$off("on-form-blur", this.onFieldBlur);
			this.$off("on-form-change", this.onFieldChange);
			this.$on("on-form-blur", this.onFieldBlur);
			this.$on("on-form-change", this.onFieldChange);
		}
		getRules(): Array<RuleItem> {
			let rls = new Array<RuleItem>();
			if (this.rules) {
				rls = rls.concat(this.rules as RuleItem[]);
			}
			if (this.FormInstance && this.FormInstance.rules) {
				const formRules = this.FormInstance.rules;
				if (formRules) {
					//	const rule = Vue.observable(formRules[this.prop]);
					const rule = JSON.parse(JSON.stringify(formRules[this.prop]));
					rls = rls.concat(rule as RuleItem[]);
					//console.info(rule);
					//	rls.push(rule);
				}
			}

			return rls;
		}
		getFilteredRule(trigger: string) {
			const rules = this.getRules();
			return rules.filter(
				rule =>
					// eslint-disable-next-line @typescript-eslint/no-explicit-any
					!(rule as any).trigger ||
					// eslint-disable-next-line @typescript-eslint/no-explicit-any
					((rule as any).trigger as string).indexOf(trigger) !== -1
			);
		}
		validate(trigger: string, callback?: Function) {
			let rules = this.getFilteredRule(trigger);

			if (!rules || rules.length === 0) {
				if (!this.required && callback) {
					callback();
					return true;
				} else {
					rules = [{ required: true }];
				}
			}

			this.validateState = "validating";
			const descriptor: Rules = {};

			descriptor[this.prop] = rules;
			const validator = new AsyncValidator(descriptor);
			const model: ValidateSource = {};
			model[this.prop] = this.fieldValue;

			validator.validate(model, { firstFields: true }, errors => {
				this.validateState = !errors ? "success" : "error";
				this.validateMessage = errors ? errors[0].message : "";
				if (callback) callback(this.validateMessage);

				if (this.FormInstance)
					this.FormInstance.$emit(
						"on-validate",
						this.prop,
						!errors,
						this.validateMessage || null
					);
			});
			this.validateDisabled = false;
		}
		resetField() {
			this.validateState = "";
			this.validateMessage = "";

			const model = this.FormInstance.model;
			const value = this.fieldValue;
			let path = this.prop;
			if (path.indexOf(":") !== -1) {
				path = path.replace(/:/, ".");
			}

			const prop = getPropByPath(model, path);

			if (Array.isArray(value)) {
				this.validateDisabled = true;
				//	prop.o[prop.k] = [].concat(this.initialValue);
			} else {
				this.validateDisabled = true;
				prop.o[prop.k] = this.initialValue;
			}
		}
		onFieldBlur() {
			this.validate("blur");
		}
		onFieldChange() {
			if (this.validateDisabled) {
				this.validateDisabled = false;
				return;
			}

			this.validate("change");
		}

		testRule() {
			const descriptor11: Rules = {
				name: [
					{
						type: "string",
						message: "not required",
						required: true
					},
					{
						type: "string",
						required: true,
						message: "not muji",
						validator: (rule, value) => value === "muji"
					}
				]
			};
			const tss = { name: "" };
			const validator11 = new AsyncValidator(descriptor11);
			console.info(descriptor11);
			console.info(tss);
			validator11.validate(tss, { firstFields: false }, errors => {
				console.info(errors);
			});
		}
	}
</script>
